package com.och.mrcp.core.sip.sdp;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SDP解析器
 * RFC 4566 - SDP: Session Description Protocol
 */
@Slf4j
public class SdpParser {
    
    // RFC 4566 Section 5 - SDP语法规则
    private static final Pattern SDP_LINE_PATTERN = Pattern.compile("^([a-z])=(.*)$", Pattern.CASE_INSENSITIVE);
    private static final Pattern MEDIA_LINE_PATTERN = Pattern.compile("^(\\w+)\\s+(\\d+)\\s+(\\S+)\\s+(\\d+)(.*)$");
    
    /**
     * 解析SDP Offer
     */
    public static SdpOffer parseOffer(String sdpBody) {
        if (sdpBody == null || sdpBody.trim().isEmpty()) {
            throw new IllegalArgumentException("SDP body cannot be null or empty");
        }
        
        SdpOffer offer = new SdpOffer();
        String[] lines = sdpBody.split("\\r?\\n");
        
        MediaDescription currentMedia = null;
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty()) continue;
            
            Matcher matcher = SDP_LINE_PATTERN.matcher(line);
            if (matcher.matches()) {
                String type = matcher.group(1).toLowerCase();
                String value = matcher.group(2);
                
                switch (type) {
                    case "v": // 版本
                        offer.setVersion(value);
                        break;
                    case "o": // 会话创建者
                        offer.setOrigin(value);
                        break;
                    case "s": // 会话名称
                        offer.setSessionName(value);
                        break;
                    case "c": // 连接信息
                        offer.setConnectionInfo(value);
                        break;
                    case "t": // 时间描述
                        offer.setTimeDescription(value);
                        break;
                    case "m": // 媒体描述
                        MediaDescription media = parseMediaLine(value);
                        if (media != null) {
                            offer.addMediaDescription(media);
                            currentMedia = media;
                        }
                        break;
                    case "a": // 属性：会话或媒体属性
                        if (currentMedia != null) {
                            currentMedia.addAttribute(value);
                        } else {
                            offer.addAttribute("a", value);
                        }
                        break;
                    default:
                        // 其他属性
                        offer.addAttribute(type, value);
                        break;
                }
            }
        }
        
        log.debug("Parsed SDP Offer: {}", offer);
        return offer;
    }
    
    /**
     * 解析媒体行
     */
    private static MediaDescription parseMediaLine(String mediaLine) {
        Matcher matcher = MEDIA_LINE_PATTERN.matcher(mediaLine);
        if (matcher.matches()) {
            MediaDescription media = new MediaDescription();
            media.setMediaType(matcher.group(1));           // 媒体类型 (audio/video)
            media.setPort(Integer.parseInt(matcher.group(2))); // 端口
            media.setTransport(matcher.group(3));           // 传输协议 (RTP/AVP)
            media.setPayloadType(Integer.parseInt(matcher.group(4))); // 负载类型
            
            // 解析格式列表
            String formats = matcher.group(5);
            if (formats != null && !formats.trim().isEmpty()) {
                String[] formatArray = formats.trim().split("\\s+");
                for (String format : formatArray) {
                    if (!format.isEmpty()) {
                        media.addFormat(Integer.parseInt(format));
                    }
                }
            }
            
            return media;
        }
        
        log.warn("Failed to parse media line: {}", mediaLine);
        return null;
    }
    
    /**
     * 生成SDP Answer
     */
    public static SdpAnswer generateAnswer(SdpOffer offer, int rtpPort, String serverIp, int mrcpPort) {
        SdpAnswer answer = new SdpAnswer();
        
        // 复制基本会话信息
        answer.setVersion(offer.getVersion());
        answer.setOrigin(offer.getOrigin());
        answer.setSessionName(offer.getSessionName());
        // 使用服务器IP作为连接信息
        if (serverIp != null && !serverIp.isEmpty()) {
            answer.setConnectionInfo("IN IP4 " + serverIp);
        } else {
            answer.setConnectionInfo(offer.getConnectionInfo());
        }
        answer.setTimeDescription(offer.getTimeDescription());
        
        // 设置RTP端口
        answer.setRtpPort(rtpPort);
        
        // 复制媒体描述，但使用服务器分配的端口
        for (MediaDescription media : offer.getMediaDescriptions()) {
            if ("audio".equalsIgnoreCase(media.getMediaType())) {
                MediaDescription answerMedia = new MediaDescription();
                answerMedia.setMediaType(media.getMediaType());
                // 计算与服务器支持的编解码交集
                List<Integer> offered = new ArrayList<>();
                offered.add(media.getPayloadType());
                offered.addAll(media.getFormats());
                Set<Integer> supported = new HashSet<>(Arrays.asList(0, 8, 9, 96)); // PCMU, PCMA, G722, L16
                List<Integer> intersection = new ArrayList<>();
                for (Integer fmt : offered) {
                    if (supported.contains(fmt)) {
                        intersection.add(fmt);
                    }
                }
                boolean peerRequestedClose = media.getPort() == 0;
                if (intersection.isEmpty()) {
                    // 没有可协商的编解码，拒绝媒体
                    answerMedia.setPort(0);
                    // 复制关键信息（如mid），但不设置方向
                    for (String attr : media.getAttributes()) {
                        String lower = attr.toLowerCase();
                        if (lower.startsWith("mid:")) {
                            answerMedia.addAttribute(attr);
                        }
                    }
                    answer.addMediaDescription(answerMedia);
                    continue;
                }
                // 有交集：选择首选并使用服务器分配端口
                answerMedia.setPort(rtpPort);
                answerMedia.setTransport(media.getTransport());
                int selected = selectPreferredPayloadType(intersection);
                answerMedia.setPayloadType(selected);
                // 其他可选格式仍附带在后面（去重）
                for (Integer fmt : intersection) {
                    if (fmt != selected) {
                        answerMedia.addFormat(fmt);
                    }
                }
                // 复制音频媒体属性（如有），过滤方向性，方向设置为 recvonly
                for (String attr : media.getAttributes()) {
                    String lower = attr.toLowerCase();
                    if (lower.startsWith("sendonly") || lower.startsWith("recvonly") ||
                        lower.startsWith("sendrecv") || lower.startsWith("inactive")) {
                        continue;
                    }
                    answerMedia.addAttribute(attr);
                }
                // 我方音频方向：对端端口为0视为保持/暂停 => inactive；否则接收客户端音频 => recvonly
                answerMedia.addAttribute(peerRequestedClose ? "inactive" : "recvonly");
                answer.addMediaDescription(answerMedia);
                // 不 break，继续查找application媒体
            } else if ("application".equalsIgnoreCase(media.getMediaType()) &&
                       media.getTransport() != null && media.getTransport().toUpperCase().contains("TCP/MRCPV2")) {
                MediaDescription answerApp = new MediaDescription();
                answerApp.setMediaType("application");
                // 使用服务器MRCP监听端口
                answerApp.setPort(mrcpPort);
                answerApp.setTransport(media.getTransport());
                answerApp.setPayloadType(media.getPayloadType());
                answerApp.getFormats().addAll(media.getFormats());
                // 根据对端setup选择被动
                String setupValue = "setup:passive";
                for (String attr : media.getAttributes()) {
                    String lower = attr.toLowerCase();
                    if (lower.startsWith("setup:")) {
                        if (lower.contains("passive")) {
                            setupValue = "setup:active"; // 对端被动，则我方主动
                        } else if (lower.contains("active")) {
                            setupValue = "setup:passive"; // 对端主动，则我方被动
                        } else if (lower.contains("actpass")) {
                            setupValue = "setup:passive";
                        }
                    }
                }
                // 基本MRCP属性
                answerApp.addAttribute(setupValue);
                answerApp.addAttribute("connection:new");
                // 透传resource/cmid等
                for (String attr : media.getAttributes()) {
                    String lower = attr.toLowerCase();
                    if (lower.startsWith("resource:") || lower.startsWith("cmid:")) {
                        answerApp.addAttribute(attr);
                    }
                }
                answer.addMediaDescription(answerApp);
            }
        }
        
        // 不再添加会话级 ptime/maxptime，避免与媒体级重复
        
        return answer;
    }

    private static int selectPreferredPayloadType(List<Integer> offered) {
        if (offered == null || offered.isEmpty()) return 0; // default PCMU
        if (offered.contains(0)) return 0;   // PCMU
        if (offered.contains(8)) return 8;   // PCMA
        if (offered.contains(9)) return 9;   // G722
        return offered.get(0);
    }
    
    /**
     * SDP Offer模型
     */
    @Data
    public static class SdpOffer {
        private String version;
        private String origin;
        private String sessionName;
        private String connectionInfo;
        private String timeDescription;
        private final List<MediaDescription> mediaDescriptions = new ArrayList<>();
        private final Map<String, String> attributes = new HashMap<>();
        
        public void addMediaDescription(MediaDescription media) {
            mediaDescriptions.add(media);
        }
        
        public void addAttribute(String name, String value) {
            attributes.put(name, value);
        }
        
        public String getAttribute(String name) {
            return attributes.get(name);
        }
    }
    
    /**
     * SDP Answer模型
     */
    @Data
    public static class SdpAnswer {
        private String version;
        private String origin;
        private String sessionName;
        private String connectionInfo;
        private String timeDescription;
        private int rtpPort;
        private final List<MediaDescription> mediaDescriptions = new ArrayList<>();
        private final Map<String, String> attributes = new HashMap<>();
        
        public void addMediaDescription(MediaDescription media) {
            mediaDescriptions.add(media);
        }
        
        public void addAttribute(String name, String value) {
            attributes.put(name, value);
        }
        
        public String getAttribute(String name) {
            return attributes.get(name);
        }
        
        /**
         * 生成SDP格式字符串 (RFC 4566 Section 5)
         */
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            
            // 会话描述
            if (version != null) sb.append("v=").append(version).append("\r\n");
            if (origin != null) sb.append("o=").append(origin).append("\r\n");
            if (sessionName != null) sb.append("s=").append(sessionName).append("\r\n");
            if (connectionInfo != null) sb.append("c=").append(connectionInfo).append("\r\n");
            if (timeDescription != null) sb.append("t=").append(timeDescription).append("\r\n");
            
            // 媒体描述
            for (MediaDescription media : mediaDescriptions) {
                sb.append("m=").append(media.getMediaType()).append(" ")
                  .append(media.getPort()).append(" ")
                  .append(media.getTransport()).append(" ")
                  .append(media.getPayloadType());
                
                // 格式列表
                for (Integer format : media.getFormats()) {
                    sb.append(" ").append(format);
                }
                sb.append("\r\n");
                
                // 媒体属性：仅在未显式提供时附加缺省 rtpmap
                boolean hasPcmu = false;
                boolean hasPcma = false;
                boolean hasG722 = false;
                for (String attr : media.getAttributes()) {
                    String lower = attr.toLowerCase();
                    if (lower.startsWith("rtpmap:0")) hasPcmu = true;
                    if (lower.startsWith("rtpmap:8")) hasPcma = true;
                    if (lower.startsWith("rtpmap:9")) hasG722 = true;
                }
                if (media.getPayloadType() == 8 && !hasPcma) {
                    sb.append("a=rtpmap:8 PCMA/8000\r\n");
                } else if (media.getPayloadType() == 0 && !hasPcmu) {
                    sb.append("a=rtpmap:0 PCMU/8000\r\n");
                } else if (media.getPayloadType() == 9 && !hasG722) {
                    sb.append("a=rtpmap:9 G722/8000\r\n");
                }
                for (String attr : media.getAttributes()) {
                    sb.append("a=").append(attr).append("\r\n");
                }
            }
            
            // 会话属性
            for (Map.Entry<String, String> attr : attributes.entrySet()) {
                sb.append("a=").append(attr.getKey()).append(":").append(attr.getValue()).append("\r\n");
            }
            
            return sb.toString();
        }
    }
    
    /**
     * 媒体描述模型
     */
    @Data
    public static class MediaDescription {
        private String mediaType;        // 媒体类型 (audio/video)
        private int port;                // 端口号
        private String transport;        // 传输协议 (RTP/AVP)
        private int payloadType;         // 负载类型
        private final List<Integer> formats = new ArrayList<>(); // 格式列表
        private final List<String> attributes = new ArrayList<>(); // 媒体属性
        
        public void addFormat(int format) {
            formats.add(format);
        }
        public void addAttribute(String attribute) {
            attributes.add(attribute);
        }
    }
}
